package com.yunbao.ioc.bd;

import com.yunbao.ioc.annotation.*;
import com.yunbao.ioc.tx.TransactionalAdviceInfo;
import com.yunbao.ioc.utils.AnnotationUtils;
import com.yunbao.ioc.utils.ClassScanner;

import java.lang.reflect.Field;
import java.util.*;

/**
 * Created by louisyuu on 2020/3/3 下午5:34
 */
public class AnnotatedBeanDefinitionReader {


    private BeanDefinitionRegistry beanDefinitionRegistry;


    /**
     * 顶有需要实例化的注解类集合
     */
    private Set<Class<?>> beanClasses = new HashSet<>();


    public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry, String... packageNames) {
        this.beanDefinitionRegistry = beanDefinitionRegistry;
        Map<String, List<Class<?>>> packageClasses = new HashMap<>();
        for (String pkName : packageNames) {
            List<Class<?>> classes = ClassScanner.getClasses(pkName);
            if (classes != null && !classes.isEmpty()) {
                packageClasses.put(pkName, classes);
            }
        }
        packageClasses.forEach((pkName, classes) -> {
            AnnotationUtils.validationAutowired(classes);
        });

        packageClasses.forEach((pkName, classes) -> {
            Set<Class<?>> beanClasses = AnnotationUtils.getBeanClasses(classes);
            if (!beanClasses.isEmpty()) {
                this.beanClasses.addAll(beanClasses);
            }
        });
        TransactionalAdviceInfo.add(beanClasses);
        packageClasses.clear();
    }


    public void loadBeanDefinitions() {
        this.beanClasses.forEach(clazz -> {
            Service service = clazz.getDeclaredAnnotation(Service.class);
            Repository repository = clazz.getDeclaredAnnotation(Repository.class);
            Component component = clazz.getDeclaredAnnotation(Component.class);
            Controller controller = clazz.getDeclaredAnnotation(Controller.class);

            String classSimpleName = clazz.getSimpleName();
            String sub = classSimpleName.substring(1);
            String pre = classSimpleName.substring(0, 1);
            String beanId = pre.toLowerCase() + sub;
            if (repository != null) {
                if (!repository.value().isEmpty()) {
                    beanId = repository.value();
                }
            } else if (service != null) {
                if (!service.value().isEmpty()) {
                    beanId = service.value();
                }
            } else if (component != null) {
                if (!component.value().isEmpty()) {
                    beanId = component.value();
                }
            } else if (controller != null) {
                if (!controller.value().isEmpty()) {
                    beanId = controller.value();
                }
            } else {
                throw new RuntimeException("Must have one component");
            }

            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setId(beanId);
            beanDefinition.setBeanClassName(clazz.getName());

            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                Autowired autowired = field.getAnnotation(Autowired.class);
                if (autowired != null) {
                    if (field.getType().isPrimitive() || field.getType().equals(String.class)) {
                        throw new RuntimeException("Can not autowired primitive type");
                    }
                    String name = field.getName();
                    String ref = field.getType().getName();
                    beanDefinition.addPropertyValues(new PropertyValue(name, ref, PropertyValue.ValueType.REFERENCE));
                }
            }
            beanDefinitionRegistry.registerBeanDefinitions(beanDefinition);
        });
        this.cleanBeanClassesCache();
    }


    public void cleanBeanClassesCache() {
        beanClasses.clear();
    }
}
